#include "stdafx.h"
#include "FileDropTarget.h"
#include <shlguid.h>
#include <shobjidl.h >

CIDropTarget::CIDropTarget(HWND hTargetWnd): 
	m_hTargetWnd(hTargetWnd),
	m_cRefCount(0), m_bAllowDrop(false),
	m_pDropTargetHelper(NULL), m_pSupportedFrmt(NULL)
{
	if(FAILED(CoCreateInstance(CLSID_DragDropHelper,NULL,CLSCTX_INPROC_SERVER,
                     IID_IDropTargetHelper,(LPVOID*)&m_pDropTargetHelper)))
		m_pDropTargetHelper = NULL;
}

CIDropTarget::~CIDropTarget()
{
	if(m_pDropTargetHelper != NULL)
	{
		m_pDropTargetHelper->Release();
		m_pDropTargetHelper = NULL;
	}
}

HRESULT STDMETHODCALLTYPE CIDropTarget::QueryInterface( /* [in] */ REFIID riid,
						/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
{
   *ppvObject = NULL;
   if (IID_IUnknown==riid || IID_IDropTarget==riid)
			 *ppvObject=this;

	if (*ppvObject != NULL)
	{
		((LPUNKNOWN)*ppvObject)->AddRef();
		return S_OK;
	}
	return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE CIDropTarget::Release( void)
{
   long nTemp;
   nTemp = --m_cRefCount;
   if(nTemp==0)
	  delete this;
   return nTemp;
}

bool CIDropTarget::QueryDrop(DWORD grfKeyState, LPDWORD pdwEffect)
{  
	DWORD dwOKEffects = *pdwEffect; 

	if(!m_bAllowDrop)
	{
	   *pdwEffect = DROPEFFECT_NONE;
	   return false;
	}
	//CTRL+SHIFT  -- DROPEFFECT_LINK
	//CTRL        -- DROPEFFECT_COPY
	//SHIFT       -- DROPEFFECT_MOVE
	//no modifier -- DROPEFFECT_MOVE or whatever is allowed by src
 	*pdwEffect = (grfKeyState & MK_CONTROL) ?
				 ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_LINK : DROPEFFECT_COPY ):
				 ( (grfKeyState & MK_SHIFT) ? DROPEFFECT_MOVE : 0 );
	if(*pdwEffect == 0) 
	{
	   // No modifier keys used by user while dragging. 
	   if (DROPEFFECT_COPY & dwOKEffects)
		  *pdwEffect = DROPEFFECT_COPY;
	   else if (DROPEFFECT_MOVE & dwOKEffects)
		  *pdwEffect = DROPEFFECT_MOVE; 
	   else if (DROPEFFECT_LINK & dwOKEffects)
		  *pdwEffect = DROPEFFECT_LINK; 
	   else 
	   {
		  *pdwEffect = DROPEFFECT_NONE;
	   }
	} 
	else
	{
	   // Check if the drag source application allows the drop effect desired by user.
	   // The drag source specifies this in DoDragDrop
	   if(!(*pdwEffect & dwOKEffects))
		  *pdwEffect = DROPEFFECT_NONE;
	}  

	return (DROPEFFECT_NONE == *pdwEffect)?false:true;
}   

HRESULT STDMETHODCALLTYPE CIDropTarget::DragEnter(
    /* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
    /* [in] */ DWORD grfKeyState,
    /* [in] */ POINTL pt,
    /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
{
	if(pDataObj == NULL)
		return E_INVALIDARG;

	if(m_pDropTargetHelper)
		m_pDropTargetHelper->DragEnter(m_hTargetWnd, pDataObj, (LPPOINT)&pt, *pdwEffect);

	m_pSupportedFrmt = NULL;
	for(size_t i =0; i<m_formatetc.size(); ++i)
	{
		m_bAllowDrop = (pDataObj->QueryGetData(&m_formatetc[i]) == S_OK)?true:false;
		if(m_bAllowDrop)
		{
			m_pSupportedFrmt = &m_formatetc[i];
			break;
		}
	}

	QueryDrop(grfKeyState, pdwEffect);
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CIDropTarget::DragOver( 
        /* [in] */ DWORD grfKeyState,
        /* [in] */ POINTL pt,
        /* [out][in] */ DWORD __RPC_FAR *pdwEffect)
{
	if(m_pDropTargetHelper)
		m_pDropTargetHelper->DragOver((LPPOINT)&pt, *pdwEffect);
	QueryDrop(grfKeyState, pdwEffect);
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CIDropTarget::DragLeave( void)
{
	if(m_pDropTargetHelper)
		m_pDropTargetHelper->DragLeave();
	
	m_bAllowDrop = false;
	m_pSupportedFrmt = NULL;
	return S_OK;
}

HRESULT STDMETHODCALLTYPE CIDropTarget::Drop(
	/* [unique][in] */ IDataObject __RPC_FAR *pDataObj,
    /* [in] */ DWORD grfKeyState, /* [in] */ POINTL pt, 
	/* [out][in] */ DWORD __RPC_FAR *pdwEffect)
{
	if (pDataObj == NULL)
		return E_INVALIDARG;	

	if(m_pDropTargetHelper)
		m_pDropTargetHelper->Drop(pDataObj, (LPPOINT)&pt, *pdwEffect);

	if(QueryDrop(grfKeyState, pdwEffect))
	{
		if(m_bAllowDrop && m_pSupportedFrmt != NULL)
		{
			STGMEDIUM medium;
			if(pDataObj->GetData(m_pSupportedFrmt, &medium) == S_OK)
			{
				if(OnDrop(m_pSupportedFrmt, medium, pdwEffect)) //does derive class wants us to free medium?
					ReleaseStgMedium(&medium);
			}
		}
	}
	m_bAllowDrop=false;
	*pdwEffect = DROPEFFECT_NONE;
	m_pSupportedFrmt = NULL;
	return S_OK;
}

